﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Navigation;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Devices;
using System.Threading;

using SplashOn_PhoneLib;
using System.Windows.Media.Imaging;
using System.ComponentModel;
using Microsoft.Xna.Framework.Input.Touch;

namespace SplashOn_Phone
{
    public partial class ColorPickerPage : PhoneApplicationPage, INotifyPropertyChanged
    {        
        GameTimer timer;

        PhotoCamera cam;
        Thread bgThread;
        volatile bool bgPleaseExit = false;        
        public event PropertyChangedEventHandler PropertyChanged;        

        // Internal YCbCr values used by the public properities.
        private byte Y { get; set; }
        private int Cb { get; set; }
        private int Cr { get; set; }

        public String colorText
        {
            get;
            set;
        }
        // For binding from the rectangle that displays the crosshairs color.
        public Brush ArgbBrush
        {
            get
            {
                int argb = YCbCrToArgb(Y, Cb, Cr);
                int r = (argb >> 16) & 0xFF;
                int g = (argb >> 8) & 0xFF;
                int b = argb & 0xFF;
                System.Diagnostics.Debug.WriteLine("R,G,B :: " + r + "," + g + "," + b);        
                colorText = "R:" + r + ", G:" + g + ", B: " + b;
                return new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, (byte)r, (byte)g, (byte)b));
            }
        }

        public Brush ArgbBrushLast1
        {
            get;
            set;
        }

        public Brush ArgbBrushLast2
        {
            get;
            set;
        }

        public Brush ArgbBrushLast3
        {
            get;
            set;
        } 
        
        public ColorPickerPage()
        {
            InitializeComponent();
            this.DataContext = this;

            timer = new GameTimer();
            timer.UpdateInterval = TimeSpan.FromTicks(333333);
            timer.Update += OnUpdate;            
        }        

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            StartTransitionFromGamePage();
            UpdateLast3Colors();
            NotifyLastColorChanged();
            try
            {
                NavigationService.RemoveBackEntry();
            }
            catch (InvalidOperationException exe)
            {
                DebugHelper.w(exe.Message);
            }

            timer.Start();

            TouchPanel.EnabledGestures = GestureType.Flick | GestureType.Tap;

            if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true) ||
                 (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing) == true))
            {                
                cam = new PhotoCamera();
                cam.Initialized += new EventHandler<CameraOperationCompletedEventArgs>(cam_Initialized);
                viewfinderBrush.SetSource(cam);
            }
            else
            {
                MessageBox.Show("This sample requires a camera.");
                CameraCrossHairs.Visibility = System.Windows.Visibility.Collapsed;
            }
            
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            timer.Stop();

            if (cam != null)
            {
                // Notify the background worker to stop processing.
                bgPleaseExit = true;
                bgThread.Join();

                // Dispose of the camera object to free memory.
                cam.Dispose();
            }
        }

        private void OnUpdate(object sender, GameTimerEventArgs e)
        {            
            HandleTouchInput();            
        }

        private void HandleTouchInput()
        {
            TouchCollection touches = TouchPanel.GetState();

            // System.Diagnostics.Debug.WriteLine(":: TouchPanel.IsGestureAvailable="+TouchPanel.IsGestureAvailable);
            while (TouchPanel.IsGestureAvailable)
            {
                GestureSample gesture = TouchPanel.ReadGesture();

                // System.Diagnostics.Debug.WriteLine(":: gesture.GestureType="+gesture.GestureType);
                switch (gesture.GestureType)
                {                   
                    case GestureType.Flick:
                        System.Diagnostics.Debug.WriteLine(":: gesture.Delta.X=" + gesture.Delta.X);
                        System.Diagnostics.Debug.WriteLine(":: gesture.Delta.Y=" + gesture.Delta.Y);
                        if (gesture.Delta.X < -1000)
                        {
                            NavigationService.Navigate(new Uri(string.Format("/GamePage.xaml"), UriKind.Relative));                            
                            StartTransitionToGamePage();
                        }
                        break;
                }
            }
        }

        void cam_Initialized(object sender, CameraOperationCompletedEventArgs e)
        {
            if (cam != null)
            {
                System.Diagnostics.Debug.WriteLine(":: CAM _ INITIALIZED");                
                // Start the background worker thread that processes the camera preview buffer frames.
                bgPleaseExit = false;
                bgThread = new Thread(colorConversionBackgroundWorker);
                bgThread.Start();
            }
        }

        private void colorConversionBackgroundWorker()
        {           
            // Grouping the property change notifications in a batch.
            List<PropertyChangedEventArgs> changeCache = new List<PropertyChangedEventArgs>();            
            changeCache.Add(new PropertyChangedEventArgs("ArgbBrush"));
            changeCache.Add(new PropertyChangedEventArgs("colorText"));

            // Obtain the YCbCr layout settings used by the camera buffer.
            var bufferLayout = cam.YCbCrPixelLayout;

            // Allocate the appropriately sized preview buffer.
            byte[] currentPreviewBuffer = new byte[bufferLayout.RequiredBufferSize];

            // Continue processing until asked to stop in OnNavigatingFrom.
            while (!bgPleaseExit)
            {
                // Get the current preview buffer from the camera.
                cam.GetPreviewBufferYCbCr(currentPreviewBuffer);

                // The output parameters used in the following method.
                byte y;
                int cr;
                int cb;

                // Extract details about the pixel where the camera crosshairs meet.
                // This location is estimated to be X=320, Y=240. Adjust as desired.
                GetYCbCrFromPixel(bufferLayout, currentPreviewBuffer, 320, 240, out y, out cr, out cb);

                // Set page-level properties to the new YCbCr values.
                Y = y;
                Cb = cb;
                Cr = cr;
                
                // System.Diagnostics.Debug.WriteLine("Y,Cr,Cb :: " + Y + "," + Cr + "," + Cb); 
                
                Dispatcher.BeginInvoke(delegate()
                {
                    // not threadsafe, but unlikely to be a problem in this case

                    // Consolidating change notifications
                    if (PropertyChanged != null)
                    {
                        foreach (var change in changeCache)
                            PropertyChanged(this, change);
                    }
                });
            }
        }

        private void GetYCbCrFromPixel(YCbCrPixelLayout layout, byte[] currentPreviewBuffer, int xFramePos, int yFramePos, out byte y, out int cr, out int cb)
        {
            // Find the bytes corresponding to the pixel location in the frame.
            int yBufferIndex = layout.YOffset + yFramePos * layout.YPitch + xFramePos * layout.YXPitch;
            int crBufferIndex = layout.CrOffset + (yFramePos / 2) * layout.CrPitch + (xFramePos / 2) * layout.CrXPitch;
            int cbBufferIndex = layout.CbOffset + (yFramePos / 2) * layout.CbPitch + (xFramePos / 2) * layout.CbXPitch;

            // The luminance value is always positive.
            y = currentPreviewBuffer[yBufferIndex];

            // The preview buffer contains an unsigned value between 255 and 0.
            // The buffer value is cast from a byte to an integer.
            cr = currentPreviewBuffer[crBufferIndex];

            // Convert to a signed value between 127 and -128.
            cr -= 128;

            // The preview buffer contains an unsigned value between 255 and 0.
            // The buffer value is cast from a byte to an integer.
            cb = currentPreviewBuffer[cbBufferIndex];

            // Convert to a signed value between 127 and -128.
            cb -= 128;
        }

        private int YCbCrToArgb(byte y, int cb, int cr)
        {
            // Individual RGB components.
            int r, g, b;

            // Used for building a 32-bit ARGB pixel.
            uint argbPixel;

            // Assumes Cb & Cr have been converted to signed values (ranging from 127 to -128).

            // Integer-only division.
            r = y + cr + (cr >> 2) + (cr >> 3) + (cr >> 5);
            g = y - ((cb >> 2) + (cb >> 4) + (cb >> 5)) - ((cr >> 1) + (cr >> 3) + (cr >> 4) + (cr >> 5));
            b = y + cb + (cb >> 1) + (cb >> 2) + (cb >> 6);

            // Clamp values to 8-bit RGB range between 0 and 255.
            r = r <= 255 ? r : 255;
            r = r >= 0 ? r : 0;
            g = g <= 255 ? g : 255;
            g = g >= 0 ? g : 0;
            b = b <= 255 ? b : 255;
            b = b >= 0 ? b : 0;

            // Pack individual components into a single pixel.
            argbPixel = 0xff000000; // Alpha
            argbPixel |= (uint)b;
            argbPixel |= (uint)(g << 8);
            argbPixel |= (uint)(r << 16);

            // Return the ARGB pixel.
            return unchecked((int)argbPixel);
        }

        private void CurentColor_Tap(object sender, RoutedEventArgs e)
        {
            DebugHelper.w(" --> " + (sender is Button));
            if (sender is Button)
            {
                Button senderButton = (Button)sender;
                if (senderButton.Background is SolidColorBrush)
                {
                    DebugHelper.w(" --> " + senderButton.Background.GetType());
                    SolidColorBrush senderButtonBrush = (SolidColorBrush)senderButton.Background;

                    ArgbBrushLast1 = senderButtonBrush;
                    SplashOnColorPicker.addBrush(senderButtonBrush);
                    UpdateLast3Colors();
                    NotifyLastColorChanged();

                    DebugHelper.w(" --> " + senderButtonBrush.Color.ToString());
                    //String colorSelectedString = senderButtonBrush.Color.ToString();
                    
                    String paramsa = "selectedColorA="+senderButtonBrush.Color.A.ToString() + 
                                    "&selectedColorR="+senderButtonBrush.Color.R.ToString() +  
                                    "&selectedColorG="+senderButtonBrush.Color.G.ToString() + 
                                    "&selectedColorB="+senderButtonBrush.Color.B.ToString();
                    DebugHelper.w(" --> " + paramsa);
                    NavigationService.Navigate(new Uri("/GamePage.xaml?" + paramsa, UriKind.Relative));
                    StartTransitionToGamePage();
                }
            }
        }

        private void LastColor_Tap(object sender, RoutedEventArgs e)
        {
            DebugHelper.w(" --> " + (sender is Button));
            if (sender is Button)
            {
                Button senderButton = (Button)sender;
                if (senderButton.Background is SolidColorBrush)
                {
                    DebugHelper.w(" --> " + senderButton.Background.GetType());
                    SolidColorBrush senderButtonBrush = (SolidColorBrush)senderButton.Background;
                    DebugHelper.w(" --> " + senderButtonBrush.Color.ToString());
                    //String colorSelectedString = senderButtonBrush.Color.ToString();

                    String paramsa = "selectedColorA=" + senderButtonBrush.Color.A.ToString() +
                                    "&selectedColorR=" + senderButtonBrush.Color.R.ToString() +
                                    "&selectedColorG=" + senderButtonBrush.Color.G.ToString() +
                                    "&selectedColorB=" + senderButtonBrush.Color.B.ToString();
                    DebugHelper.w(" --> " + paramsa);
                    NavigationService.Navigate(new Uri("/GamePage.xaml?" + paramsa, UriKind.Relative));
                    StartTransitionToGamePage();
                }
            }
        }

        private void UpdateLast3Colors()
        {
            SolidColorBrush[] last3Colors = SplashOnColorPicker.getLast3Colors();
            ArgbBrushLast1 = last3Colors[0];
            ArgbBrushLast2 = last3Colors[1];
            ArgbBrushLast3 = last3Colors[2];
        }

        private void NotifyLastColorChanged()
        {
            List<PropertyChangedEventArgs> changeCache = new List<PropertyChangedEventArgs>();
            changeCache.Add(new PropertyChangedEventArgs("ArgbBrushLast1"));
            changeCache.Add(new PropertyChangedEventArgs("ArgbBrushLast2"));
            changeCache.Add(new PropertyChangedEventArgs("ArgbBrushLast3"));
            Dispatcher.BeginInvoke(delegate()
            {
                if (PropertyChanged != null)
                {
                    foreach (var change in changeCache)
                        PropertyChanged(this, change);
                }
            });
        }

        private void StartTransitionToGamePage()
        {
            SlideTransition slideTransition = new SlideTransition();
            slideTransition.Mode = SlideTransitionMode.SlideLeftFadeOut;

            PhoneApplicationPage phoneApplicationPage =
            (PhoneApplicationPage)(((PhoneApplicationFrame)Application.Current.RootVisual)).Content;

            ITransition transition = slideTransition.GetTransition(phoneApplicationPage);
            transition.Completed += delegate
            {
                transition.Stop();
            };
            transition.Begin();
        }

        private void StartTransitionFromGamePage()
        {
            SlideTransition slideTransition = new SlideTransition();
            slideTransition.Mode = SlideTransitionMode.SlideRightFadeIn;

            PhoneApplicationPage phoneApplicationPage =
            (PhoneApplicationPage)(((PhoneApplicationFrame)Application.Current.RootVisual)).Content;

            ITransition transition = slideTransition.GetTransition(phoneApplicationPage);
            transition.Completed += delegate
            {
                transition.Stop();
            };
            transition.Begin();
        }

        private void button4_Click(object sender, RoutedEventArgs e)
        {

        }
    }
}